#pragma once

#include <iostream>
#include <string>
#include <functional>

#include <unistd.h>

#include "Sock.hpp"
#include "Protocol.hpp"


namespace tcp_cli
{
    using namespace TCP; 
    using namespace protocol;

    class Client
    {
        using func_t = std::function<Request(const std::string&)>; //处理函数
    public:
        Client(func_t func,const std::string& ip,const uint16_t& port)
            :sc(ip,port)
            ,_func(func)
        {}

        void InitClient()
        {
            sc.Socket();

            int count = 5;
            while(sc.Connect() < 0)
            {   
                std::cout<<"正在进行第"<<count--<<"次重新连接..."<<std::endl;
                if(count == 0) break;
            }
            if(count > 0) std::cout<<"连接成功!"<<std::endl;
            else
            {
                std::cout<<"连接失败!"<<std::endl;
                exit(-1);
            }
        }

        void Start()
        {
            int Server_Fd = sc.GetSockFd();
            std::string inbuffer;
            while(true)
            {
                std::string readbuf;
                std::cout<<"Enter> ";
                getline(std::cin,readbuf);
                if(readbuf.find("quit") != std::string::npos) break;
                
                //1.处理输入数据-建立请求包
                Request req = _func(readbuf);


                //2.序列化请求包并输出为string
                std::string send_string;
                req.Serialize(&send_string);

                //3.为序列化的数据加上报头
                send_string = AddHeader(send_string);


                //4.发送给服务器
                send(sc.GetSockFd(),send_string.c_str(),send_string.size(),0);

                //5.获取响应
                std::string package;
                int n = 0;
    START:
                n = ReadPackage(sc.GetSockFd(),inbuffer,&package); //获取数据 直到是一个完整的数据包
                if(n < 0) break; //n<0表示服务器关闭了
                else if(n == 0) goto START; //如果没有读取完整 则返回去读取
                


                //6.分割数据包
                package = RemoveHeader(package,n); //注意 读取了一个完整的包 返回的是有效载荷的字节大小


                //7.形成响应包并反序列化
                Response resp;
                resp.Deserialize(package);
                resp.Print();
            }
            close(sc.GetSockFd());
        }

    private:
        SockClient sc;
        func_t _func;
    };
}